package org.example.box;

import org.apache.pdfbox.Loader;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.io.IOUtils;
import org.apache.pdfbox.pdmodel.PDDocument;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.*;
import org.apache.pdfbox.pdmodel.interactive.digitalsignature.visible.*;
import org.bouncycastle.cert.jcajce.JcaCertStore;
import org.bouncycastle.cms.CMSSignedData;
import org.bouncycastle.cms.CMSSignedDataGenerator;
import org.bouncycastle.cms.jcajce.JcaSignerInfoGeneratorBuilder;
import org.bouncycastle.operator.ContentSigner;
import org.bouncycastle.operator.jcajce.JcaContentSignerBuilder;
import org.bouncycastle.operator.jcajce.JcaDigestCalculatorProviderBuilder;
import org.example.jida.JiDaSigUtil;
import org.example.util.Image;
import org.example.util.JITUtil;
import org.example.util.KeyWords;
import org.example.util.PDFUtil;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.security.KeyStore;
import java.security.PrivateKey;
import java.security.cert.Certificate;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Enumeration;

public class Sign implements SignatureInterface {
    private static final String IN_DIR = "src/main/resources/signature/";
    private static final String OUT_DIR = "target/test-output/";
    private static final String KEYSTORE_PATH = IN_DIR + "keystore.p12";
    //private static final String JPEG_PATH = IN_DIR + "stamp.jpg";
    private static final String JPEG_PATH = IN_DIR + "sig.png";
    private static final String PJPEG_PATH = IN_DIR + "stamp.jpg";
    private static final String PASSWORD = "123456";
    private static final String TSA_RESPONSE = "tsa_response.asn1";
    private static final String SIMPLE_FORM_FILENAME = "target/TestCreateSignatureSimpleForm.pdf";

    private static CertificateFactory certificateFactory = null;
    private static KeyStore keyStore = null;
    private static Certificate certificate;
    private static String tsa;

    static {
        new File(OUT_DIR).mkdirs();
    }

    public static void main(String[] args) throws Exception {
        Sign sign = new Sign();
        sign.signpdf();

        //String oldString = "MIIEeAYKKoEcz1UGAQQCAqCCBGgwggRkAgEBMQwwCgYIKoEcz1UBgxEwDAYKKoEcz1UGAQQCAaCCA28wggNrMIIDEKADAgECAgVGACgwIzAMBggqgRzPVQGDdQUAMFwxCzAJBgNVBAYTAkNOMTAwLgYDVQQKDCdDaGluYSBGaW5hbmNpYWwgQ2VydGlmaWNhdGlvbiBBdXRob3JpdHkxGzAZBgNVBAMMEkNGQ0EgQUNTIFNNMiBPQ0EzMTAeFw0yMjExMjEwOTEzNDJaFw0yNzExMjYwNTU4NDRaMIGeMQswCQYDVQQGEwJDTjEbMBkGA1UECgwSQ0ZDQSBBQ1MgU00yIE9DQTMxMRMwEQYDVQQLDApDRkNBIE9DQTMxMRkwFwYDVQQLDBBPcmdhbml6YXRpb25hbC0xMUIwQAYDVQQDDDlDRkNBQOmYs+WFieWGnOS4muebuOS6kuS/nemZqeWFrOWPuEBOOTEyMzAxMDA3MTA5MzMxNDdQQDEwWTATBgcqhkjOPQIBBggqgRzPVQGCLQNCAAT3PUID+weRlBQPwbxDfllWqE6usRLYJXRhefAI8y8tpRFdFcVRVOHlkAXPVEGI4jKbHooHTV4SdZgdf4Bn6yhDo4IBeDCCAXQwbAYIKwYBBQUHAQEEYDBeMCgGCCsGAQUFBzABhhxodHRwOi8vb2NzcC5jZmNhLmNvbS5jbi9vY3NwMDIGCCsGAQUFBzAChiZodHRwOi8vY3JsLmNmY2EuY29tLmNuL29jYTMxL29jYTMxLmNlcjAfBgNVHSMEGDAWgBQI2NEmxEh9nOysmOnxf2K5gM6pRTAMBgNVHRMBAf8EAjAAMEgGA1UdIARBMD8wPQYIYIEchu8qAQQwMTAvBggrBgEFBQcCARYjaHR0cDovL3d3dy5jZmNhLmNvbS5jbi91cy91cy0xNC5odG0wPQYDVR0fBDYwNDAyoDCgLoYsaHR0cDovL2NybC5jZmNhLmNvbS5jbi9vY2EzMS9TTTIvY3JsMjUzNi5jcmwwDgYDVR0PAQH/BAQDAgbAMB0GA1UdDgQWBBQq2g3JUbrY1DZ/RbZEXvaIPmzh5DAdBgNVHSUEFjAUBggrBgEFBQcDAgYIKwYBBQUHAwQwDAYIKoEcz1UBg3UFAANHADBEAiBcETFcDPHWi5b3agUc+NZ486YQfsIMm44xGQykgd+fSAIgcGJm5zwh35qF4AwBgqf08tdM4pLGUO51r6MR7VLj0YYxgc8wgcwCAQEwZTBcMQswCQYDVQQGEwJDTjEwMC4GA1UECgwnQ2hpbmEgRmluYW5jaWFsIENlcnRpZmljYXRpb24gQXV0aG9yaXR5MRswGQYDVQQDDBJDRkNBIEFDUyBTTTIgT0NBMzECBUYAKDAjMAoGCCqBHM9VAYMRMAsGCSqBHM9VAYItAQRHMEUCIBytNZn1TyaTKp+B4IN5jAcbsf/Cs3/0pRNmqsPAAXNKAiEAo43nMhIPuNuPWfXGCkw7lQTjN57+zujESHPxPRkco1Y=";
        //byte[] decode = Base64.decode(oldString);
        //byte[] signatureBytes = Hex.getBytes(Base64.decode(oldString));
    }

    private void signpdf() throws IOException {
        //String inPath = "D:\\ert.pdf";
        String inPath = "D:\\zhong.pdf";
        PDDocument doc = Loader.loadPDF(new File(inPath));
        FileInputStream fis = new FileInputStream(JPEG_PATH);
        FileInputStream fis2 = new FileInputStream(JPEG_PATH);
        FileInputStream pis = new FileInputStream(PJPEG_PATH);
        FileInputStream pis2 = new FileInputStream(PJPEG_PATH);

        ArrayList<Image> images = new ArrayList<>();
        //获取关键字坐标对象
        KeyWords keywords = new KeyWords("投保人签章：", true, 1, 2, doc);
        KeyWords keywords1 = new KeyWords("联系人", true, 1, 2, doc);
        //获取签名位置信息
        KeyWords keywordsSig = new KeyWords("投保人签章：", true, doc);
        //
        Image image = new Image(JITUtil.toByteArray(fis), keywords, false);
        Image image2 = new Image(JITUtil.toByteArray(fis2), keywords1, false);
        images.add(image);
        images.add(image2);

        byte[] bytes = PDFUtil.addImageToPDF(doc, images);

        Image imageSig = new Image(pis.readAllBytes(), -50, keywordsSig, true);
        bytes = PDFUtil.signPdf(bytes, imageSig);
        File destFile = new File(OUT_DIR + "sign_me_1236.pdf");
        FileOutputStream fos = new FileOutputStream(destFile);

        fos.write(bytes);
        fos.close();
/*
        //
        //

        ByteArrayInputStream stream = new ByteArrayInputStream(imageSig.getImageByte());

        PDVisibleSignDesigner visibleSignDesigner = new PDVisibleSignDesigner(doc, stream, imageSig.getIndexPage());//TODO 优化自定义签名页  和 关键字签名页 处理
        visibleSignDesigner.zoom(imageSig.getZoom());
        //获取居中图片坐标位置
        //visibleSignDesigner.get

        //KeyWords keywordsSig = new KeyWords("投保人签章：", true, doc);
        //Image imageSig = new Image(Image.BufferedImageToByte(visibleSignDesigner.getImage()), -50, keywordsSig, true);
        //修改图片方位
        //imageSig.setBiasedPosition(Image.BiasedPosition.LEFT);
        //imageSig.reCompute();

        //float[] imageByCenter =PDFUtil.getKeyCoordinates("投保人签章：",true,
        //        1,KeyWords.Coordinate.CENTER,visibleSignDesigner.getWidth(),visibleSignDesigner.getHeight(),doc);
        visibleSignDesigner.xAxis(imageSig.getX()).yAxis(imageSig.getY()).adjustForRotation();
        //visibleSignDesigner.xAxis(288L).yAxis(94L).adjustForRotation();

        PDVisibleSigProperties visibleSignatureProperties = new PDVisibleSigProperties();
        visibleSignatureProperties.signerName("epolicy")
                .signerLocation("阳光农业相互保险公司")
                .signatureReason("电子保单生成")
                .page(imageSig.getIndexPage())
                .visualSignEnabled(true)
                .setPdVisibleSignature(visibleSignDesigner);
        // this builds the signature structures in a separate document  将可见的pdf签名样式的流数据进行标准创建后返回，并保存到visibleSignatureProperties 对象中
        visibleSignatureProperties.buildSignature();//重点在这里，将上面的所有签名图片有关信息进行处理成 InputStream 流的形式，供 SignatureOptions 使用

        PDSignature signature = new PDSignature();
        signature.setFilter(COSName.getPDFName("CFCA.TrustSignPDF"));
        //signature.setFilter(PDSignature.FILTER_CFCA_TRUSTSIGNPDF);//此方法需要修改源代码
        signature.setSubFilter(COSName.getPDFName("cfca.sm2.pkcs7.detached"));
        //signature.setSubFilter(PDSignature.SUBFILTER_CFCA_SM2_PKCS7_DETACHED);//此方法需要修改源代码
        signature.setName(visibleSignatureProperties.getSignerName());
        signature.setLocation(visibleSignatureProperties.getSignerLocation());
        signature.setReason(visibleSignatureProperties.getSignatureReason());
        // the signing date, needed for valid signature
        signature.setSignDate(Calendar.getInstance());

        //设置签名样式
        SignatureOptions signatureOptions = new SignatureOptions();
        signatureOptions.setVisualSignature(visibleSignatureProperties.getVisibleSignature());
        signatureOptions.setPage(visibleSignatureProperties.getPage() - 1);
        doc.addSignature(signature, signatureOptions);
        //签名不可见
        //doc.addSignature(signature, signatureInterface);

        ////使用内部签名机制
        //// write incremental (only for signing purpose)
        ////new File("target/test-output").mkdirs();
        //File destFile = new File(OUT_DIR + "sign_me_1234.pdf");
        //FileOutputStream fos = new FileOutputStream(destFile);
        //doc.saveIncremental(fos);

        //使用外部签名机制

        ByteArrayOutputStream signPdfData = new ByteArrayOutputStream();

        File destFile = new File(OUT_DIR + "sign_me_1236.pdf");
        FileOutputStream fos = new FileOutputStream(destFile);
        ExternalSigningSupport externalSigning = doc.saveIncrementalForExternalSigning(signPdfData);//这一步处理完之后，整个pdf文档结构就已经全部构建完毕，仅仅缺少签名的信息值没有填充
        //从外部获取签名数据
        byte[] cmsSignature = JiDaSigUtil.JITSign(externalSigning.getContent());//使用这正方式获取签名数据流（实际上获取到的数据流是：除了没有添加签名值外，其他所有数据都已经处理完毕了的数据，最后只需要再写如签名值，就和签名后的PDF文件一模一样的数据流）
        externalSigning.setSignature(cmsSignature);

        fos.write(signPdfData.toByteArray());

        IOUtils.closeQuietly(signatureOptions);
        IOUtils.closeQuietly(doc);
        IOUtils.closeQuietly(signPdfData);*/
    }


    @Override
    public byte[] sign(InputStream content) throws IOException {
        // cannot be done private (interface)
        //return JITUtil.exSign(content);//使用外部
        return this.inSign(content);
    }


    public byte[] inSign(InputStream content) throws IOException {
        try {
            KeyStore keystore = KeyStore.getInstance("PKCS12");//创建一个密钥库实例
            keystore.load(Files.newInputStream(Paths.get(KEYSTORE_PATH)), PASSWORD.toCharArray());//加载密钥库
            //String alias = "myalias";
            //KeyStore.PrivateKeyEntry pkEntry = (KeyStore.PrivateKeyEntry) keystore.getEntry(alias, new KeyStore.PasswordProtection(PASSWORD.toCharArray()));
            // 获取私钥
            //PrivateKey privateKey = pkEntry.getPrivateKey();
            // 获取证书链
            //Certificate[] certificateChain = pkEntry.getCertificateChain();
            //获取证书
            //Certificate myCert = keystore.getCertificate(alias);
            //获取公钥
            //PublicKey publicKey = myCert.getPublicKey();
            //String tsaUrl = null;

            PrivateKey privateKey = null;
            Certificate[] certificateChain = null;

            Enumeration<String> aliases = keystore.aliases();
            String alias;
            Certificate cert = null;
            while (cert == null && aliases.hasMoreElements()) {
                alias = aliases.nextElement();
                privateKey = (PrivateKey) keystore.getKey(alias, PASSWORD.toCharArray());
                Certificate[] certChain = keystore.getCertificateChain(alias);
                if (certChain != null) {
                    certificateChain = certChain;
                    cert = certChain[0];
                    //if (cert instanceof X509Certificate) {
                    //    // avoid expired certificate
                    //    ((X509Certificate) cert).checkValidity();
                    //
                    //    SigUtils.checkCertificateUsage((X509Certificate) cert);
                    //}
                }
            }

            CMSSignedDataGenerator gen = new CMSSignedDataGenerator();
            X509Certificate cert2 = (X509Certificate) certificateChain[0];
            ContentSigner sha1Signer = new JcaContentSignerBuilder("SHA256WithRSA").build(privateKey);
            gen.addSignerInfoGenerator(new JcaSignerInfoGeneratorBuilder(new JcaDigestCalculatorProviderBuilder().build()).build(sha1Signer, cert2));
            gen.addCertificates(new JcaCertStore(Arrays.asList(certificateChain)));
            CMSProcessableInputStream msg = new CMSProcessableInputStream(content);
            CMSSignedData signedData = gen.generate(msg, false);
            //if (tsaUrl != null && !tsaUrl.isEmpty()) {
            //    ValidationTimeStamp validation = new ValidationTimeStamp(tsaUrl);
            //    signedData = validation.addSignedTimeStamp(signedData);
            //}
            return signedData.getEncoded();
        } catch (Exception e) {
            throw new IOException(e);
        }
    }

}
